`include "mycpu.h"

module wb_stage(
    input                           clk           ,
    input                           reset         ,
    input  [5:0                  ]  ext_int_in    ,
    //allowin
    output                          ws_allowin    ,
    //from ms
    input                           ms_to_ws_valid,
    input  [`MS_TO_WS_BUS_WD -1:0]  ms_to_ws_bus  ,
    //to rf: for write back
    output [`WS_TO_RF_BUS_WD -1:0]  ws_to_rf_bus  ,
    //to fs
    output [`WS_TO_FS_BUS_WD -1:0]  ws_to_fs_bus  ,
    //to es
    output [`WS_TO_ES_BUS_WD -1:0]  ws_to_es_bus  ,
    output                          ws_flush      ,
    output                          c0_status_exl ,
    //tlb
    output [7:0                  ]  asid          ,
    //trace debug interface
    output [31:0] debug_wb_pc     ,
    output [ 3:0] debug_wb_rf_wen ,
    output [ 4:0] debug_wb_rf_wnum,
    output [31:0] debug_wb_rf_wdata,
    //tlb write port
    output                      tlb_we,
    output [ 3:0] w_index, 
    output [18:0]               w_vpn2, 
    output [ 7:0]               w_asid, 
    output                      w_g, 
    output [19:0]               w_pfn0,
    output [ 2:0]               w_c0, 
    output                      w_d0, 
    output                      w_v0, 
    output [19:0]               w_pfn1, 
    output [ 2:0]               w_c1, 
    output                      w_d1, 
    output                      w_v1,
    //tlb read port
    output [3:0] r_index, 
    input [18:0]                r_vpn2, 
    input [ 7:0]                r_asid, 
    input                       r_g, 
    input [19:0]                r_pfn0, 
    input [ 2:0]                r_c0, 
    input                       r_d0, 
    input                       r_v0, 
    input [19:0]                r_pfn1, 
    input [ 2:0]                r_c1, 
    input                       r_d1, 
    input                       r_v1
);

reg         ws_valid;
wire        ws_ready_go;

/* exceptions */
wire ms_ex;
wire ws_ex;
wire [4:0] ms_excode;
wire [4:0] ws_excode;
wire bd;
wire [31:0] next_pc;
/* priviledged instructions */
wire [ 7:0] ws_c0_addr;
wire        ws_op_mfc0;
wire        ws_op_mtc0;
wire [31:0] ws_c0_rdata;
wire        eret_flush;
wire [31:0] epc;
wire        ws_op_tlbp;
wire        ws_op_tlbwi;
wire        ws_op_tlbr;
wire        inst_retake;
wire        ws_tlb_refill;

reg [`MS_TO_WS_BUS_WD -1:0] ms_to_ws_bus_r;
wire        ws_gr_we;
wire [ 4:0] ws_dest;
wire [31:0] ws_final_result;
wire [31:0] ws_result;
wire [31:0] ws_pc;
assign {ws_tlb_refill  ,  //92:92
        inst_retake    ,  //91:91
        ws_op_tlbp     ,  //90:90
        ws_op_tlbr     ,  //89:89
        ws_op_tlbwi    ,  //88:88
        bd             ,  //87:87
        ms_excode      ,  //86:82
        ms_ex          ,  //81:81
        eret_flush     ,  //80:80
        ws_op_mfc0     ,  //79:79
        ws_op_mtc0     ,  //78:78
        ws_c0_addr     ,  //77:70
        ws_gr_we       ,  //69:69
        ws_dest        ,  //68:64
        ws_result      ,  //63:32
        ws_pc             //31:0
       } = ms_to_ws_bus_r;

wire        rf_we;
wire [4 :0] rf_waddr;
wire [31:0] rf_wdata;
assign ws_to_rf_bus = {rf_we   ,  //37:37
                       rf_waddr,  //36:32
                       rf_wdata   //31:0
                      };

assign ws_ready_go = 1'b1;
assign ws_allowin  = !ws_valid || ws_ready_go;
always @(posedge clk) begin
    if (reset) begin
        ws_valid <= 1'b0;
    end
    else if (ws_flush) begin
        ws_valid <= 1'b0;
    end
    else if (ws_allowin) begin
        ws_valid <= ms_to_ws_valid;
    end

    if (ws_flush)
        ms_to_ws_bus_r <= 93'd0;
    else if (ms_to_ws_valid && ws_allowin)
        ms_to_ws_bus_r <= ms_to_ws_bus;
end

assign ws_final_result = ws_op_mfc0? ws_c0_rdata: ws_result;
assign rf_we    = ws_gr_we && ws_valid && !ws_ex && !inst_retake;
assign rf_waddr = ws_dest;
assign rf_wdata = ws_final_result;
// debug info generate
assign debug_wb_pc       = ws_pc;
assign debug_wb_rf_wen   = {4{rf_we}};
assign debug_wb_rf_wnum  = ws_dest;
assign debug_wb_rf_wdata = ws_final_result;

/* privileged instruction */
wire        has_int;
wire [18:0] vpn2;
cp0 cp0_regs(
    .clock      (clk),
    .reset      (reset),
    .ext_int_in (ext_int_in),
    .wb_valid   (ws_valid),
    .op_mtc0    (ws_op_mtc0),
    .op_tlbp    (ws_op_tlbp),
    .op_tlbr    (ws_op_tlbr),
    .inst_retake(inst_retake),
    .wb_ex      (ws_ex),
    .wb_bd      (bd),
    .wb_excode  (ws_excode),
    .wb_badvaddr(ws_result),
    .wb_pc      (ws_pc),
    .eret_flush (eret_flush),
    .c0_addr    (ws_c0_addr),
    .c0_wdata   (ws_result),
    .c0_rdata   (ws_c0_rdata),
    .epc        (epc),
    .exl        (c0_status_exl),
    .has_int    (has_int),
    .asid       (asid),
    .vpn2       (vpn2),

    .w_index    (w_index), 
    .w_vpn2     (w_vpn2), 
    .w_asid     (w_asid), 
    .w_g        (w_g), 
    .w_pfn0     (w_pfn0),
    .w_c0       (w_c0), 
    .w_d0       (w_d0), 
    .w_v0       (w_v0), 
    .w_pfn1     (w_pfn1),
    .w_c1       (w_c1), 
    .w_d1       (w_d1), 
    .w_v1       (w_v1),

    .r_index    (r_index), 
    .r_vpn2     (r_vpn2), 
    .r_asid     (r_asid), 
    .r_g        (r_g), 
    .r_pfn0     (r_pfn0),
    .r_c0       (r_c0), 
    .r_d0       (r_d0), 
    .r_v0       (r_v0), 
    .r_pfn1     (r_pfn1),
    .r_c1       (r_c1), 
    .r_d1       (r_d1), 
    .r_v1       (r_v1)
);
assign tlb_we = ws_op_tlbwi && ws_valid && !ws_ex && !inst_retake;

/* exceptions */
assign ws_ex = ms_ex && ws_valid;
assign ws_excode = ms_excode;
assign ws_flush = (ws_valid && eret_flush) | (ws_ex && ~c0_status_exl) | (ws_valid & inst_retake);
assign next_pc = {32{eret_flush}} & epc
               | {32{ws_ex &&  ws_tlb_refill}} & `EXCEPTION_ENTRY1
               | {32{ws_ex && !ws_tlb_refill}} & `EXCEPTION_ENTRY2
               | {32{inst_retake}} & ws_pc;

assign ws_to_fs_bus = {has_int,     //32:32
                       next_pc      //31:0
                      };
wire ws_entryhi_we;
assign ws_entryhi_we = ws_op_mtc0 && (ws_c0_addr == `CR_ENTRYHI) && ws_valid;
assign ws_to_es_bus = {ws_entryhi_we,   //19:19
                       vpn2             //18:0
                      };

endmodule
